package registnet.base.student.peer;

import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

import org.zkoss.zk.ui.Desktop;
import org.zkoss.zk.ui.DesktopUnavailableException;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.util.Clients;

import registnet.base.source.ChecklistSource;
import registnet.base.student.Student;
import registnet.dao.CourseDemandPeer;
import registnet.dao.StudentRecommendedCourse;

/**
 * A class that handles processes which involves a set of students.
 */
public class StudentPeer extends Thread{
	
	/**
	 * A numerical representation of the progress of the pre-enlistment process.
	 */
	private double progress = 0d;
	
	/**
	 * A <code>String</code> representation of the total time taken to do the pre-enlistment process.
	 */
	private String totalTimeDisp = "";
	
	/**
	 * The desktop used in the GUI.
	 */
	private final Desktop desktop;
	
	/**
	 * Default constructor
	 */
	public StudentPeer(Desktop desktop){
		progress = 0d;
		totalTimeDisp = "";
		this.desktop = desktop;
	}
	
	@Override
	public void run(){
		try{
			doPreregistration();
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			desktop.enableServerPush(false);
		}
		return;
	}
	
	/**
	 * Activate all students in the database
	 */
	public void activateAllStudents(){
		registnet.dao.StudentPeer daoStudentPeer = new registnet.dao.StudentPeer();
		ListIterator<registnet.dao.Student> li = daoStudentPeer.getAllStudents().listIterator();
		
		while(li.hasNext()){
			registnet.dao.Student daoStudent = li.next();
			if(new registnet.dao.GradePeer().getGradeByStudent(daoStudent.getStudentId(),1,2008).size() > 0)
				daoStudentPeer.updateStudent(daoStudent.getStudentId(), 1);
		}
	}
	
	/**
	 * Preregistration: Determines the standing, classification, priority and
	 * the recommended courses of the active students; and then computes the demand
	 * for each courses for the enlistment. 
	 * @throws InterruptedException 
	 * @throws DesktopUnavailableException 
	 * 
	 */
	public void doPreregistration() throws DesktopUnavailableException, InterruptedException{
		registnet.dao.StudentPeer daoStudentPeer = new registnet.dao.StudentPeer();
		List<registnet.dao.Student> studentList = daoStudentPeer.getAllActiveStudents();
		ListIterator<registnet.dao.Student> liStudent = studentList.listIterator();
		Date initialDate;
		Date finalDate;
		progress = 0d;
		Executions.activate(desktop);
		Clients.showBusy("Please wait. "+ ((int) progress * 100) +"% complete.", true);
		Executions.deactivate(desktop);
		
		initialDate = new Date();
		int ctr = 0; int sizeS = studentList.size();
		while(liStudent.hasNext()){
			if(studentList.size()==0) progress = .5;
			else progress = (ctr / (double)sizeS) * .5;
			if(((int) progress * 100) % 5 == 0){
				Executions.activate(desktop);
				Clients.showBusy("Please wait. "+ ((int) progress * 100) +"% complete.", true);
				Executions.deactivate(desktop);
			}
			
			registnet.dao.Student daoStudent = liStudent.next();
			
			/*
			 * initialize student
			 */
			Student student = new Student(daoStudent.getStudentNumber());
			student.setChecklist(new ChecklistSource(student.getStudentNumber()).getChecklist());
			
			/*
			 * determine student standing
			 */
//			student.computeStanding(2005,1);
//			student.computeStanding(2005,2);
//			student.computeStanding(2006,3);
//			student.computeStanding(2006,1);
//			student.computeStanding(2006,2);
//			student.computeStanding(2007,3);
//			student.computeStanding(2007,1);
//			student.computeStanding(2007,2);
//			student.computeStanding(2008,3);
			student.computeStanding();
			
			/*
			 * determine student classification
			 */
			student.computeClassification();
			
			/*
			 * determine student priority
			 */
			student.computePriority();
			
			/*
			 * determine recommended courses
			 */
			student.getChecklist().determineRecommendedCourses();
			ctr++;
		}
		
		/*
		 * compute course demand
		 */
		//compute demands
		ctr = 0;
		Map<Integer, Integer> map = new HashMap<Integer, Integer>();
		List<StudentRecommendedCourse> srcList = new registnet.dao.StudentRecommendedCoursePeer().getAllRecommendedCourses();
		ListIterator<StudentRecommendedCourse> liSRC = srcList.listIterator();
		while(liSRC.hasNext()){
			if(srcList.size()==0) progress = .95;
			else progress = ((ctr / (double)srcList.size()) * .45) + .5;
			if((int)progress % 5 == 0){
				Executions.activate(desktop);
				Clients.showBusy("Please wait. "+ ((int) progress * 100) +"% complete.", true);
				Executions.deactivate(desktop);
			}
			StudentRecommendedCourse src = liSRC.next();
			if(map.containsKey(src.getCourseId()))
				map.put(src.getCourseId(), map.get(src.getCourseId())+1);
			else
				map.put(src.getCourseId(), 1);
			ctr++;
		}
		//update database
		ctr = 0;
		CourseDemandPeer daoCourseDemandPeer = new CourseDemandPeer();
		daoCourseDemandPeer.deleteAllCourseDemands();
		Iterator<Integer> liInteger = map.keySet().iterator();
		while(liInteger.hasNext()){
			if(map.keySet().size()==0) progress = .1;
			else progress = ((ctr / (double)map.keySet().size()) * .05) + .95;
			if((int)progress % 5 == 0){
				Executions.activate(desktop);
				Clients.showBusy("Please wait. "+ ((int) progress * 100) +"% complete.", true);
				Executions.deactivate(desktop);
			}
			Integer courseId = liInteger.next();
			daoCourseDemandPeer.createNewCourseDemand(courseId, map.get(courseId));
			ctr++;
		}
		
		finalDate = new Date();
		long totalTime = finalDate.getTime()-initialDate.getTime();
		System.out.println("milliseconds: "+(totalTime%1000));
		System.out.println("seconds: "+(int)((totalTime/1000)%60));
		System.out.println("minutes: "+(int)((totalTime/60000)%60));
		System.out.println("hours: "+(int)((totalTime/3600000)%60));
		
		totalTimeDisp = ""+(int)((totalTime/3600000)%60)+"H:"+(int)((totalTime/60000)%60)+"M:"+(int)((totalTime/1000)%60)+"S";
		
		/*
		 * Prepares the database
		 */
		new registnet.dao.CourseOfferingSlotPeer().deleteAllCourseOfferingSlot();
		new registnet.dao.SlotPeer().deleteAllSlots();;
		
	}

	/**
	 * Returns the progress of the pre-enlistment process.
	 * @return the progress of the pre-enlistment process
	 */
	public Double getProgress() {
		return progress;
	}

	/**
	 * Returns the total time taken by the pre-enlistment process.
	 * @return the total time taken by the pre-enlistment process
	 */
	public String getTotalTimeDisp() {
		return totalTimeDisp;
	}
	
}
